package fileservice

import (
	"fmt"
	"gmap-backend/internal/config"
	"gmap-backend/internal/server/manager"
	"gmap-backend/internal/server/serviceimpl/runnerservice"
	"gmap-backend/internal/server/vo"
	"gmap-backend/internal/util/pathutil"
	"path/filepath"
	"strings"
)

// OPENER_PHOTO 内置的打开方式，图片传真查看器
const OPENER_PHOTO = "photo"

// OPTION_WRAP - 如果内容中有空格，则双引号包裹，否则不处理
const OPTION_WRAP = "wrap"

// OPTION_FORCEWRAP - 强制双引号包裹，无论是否有空格
const OPTION_FORCEWRAP = "force-wrap"

// OPTION_WRAPIN - 用双绰号包裹路径中有空格的项，比如：d:\a\b b\c.txt -> d:\a\"b b"\c.txt
const OPTION_WRAPIN = "wrap-in"

// RunFileOrDir 运行文件或打开目录
// 对于目录，使用explorer打开
// 对于文件，由于不确定文件是否是直接可执行的文件，所以统一用cmd /c打开
func RunFileOrDir(appConfig *config.AppConfig, req *vo.PathReq, bins [][]byte) {
	parseResult := pathutil.ParseValidPath(req.Path, req.CtxDir)
	if !parseResult.Succ {
		panic(fmt.Sprintf("未识别到有效路径：\r\n%s", req.Path))
	}
	if parseResult.File {
		// 指定文件是cmd，直接调用cmdopen
		if strings.TrimSpace(strings.ToLower(pathutil.GetCmdPath())) == strings.TrimSpace(strings.ToLower(parseResult.ValidPath)) {
			runnerservice.OpenCmdWithWorkDir(appConfig, &vo.PathReq{Path: parseResult.WorkDir}, bins)
			return
		}

		cmd := fmt.Sprintf(`"%s"`, parseResult.ValidPath)

		commandMode := req.ForceCmdMode || pathutil.IsBatFile(parseResult.ValidPath)
		codePage := manager.DEFAULT_CODE_PAGE
		if commandMode && req.CodePage > 0 {
			codePage = req.CodePage
		}
		batPath := manager.SaveCmdFileWithCodePage(appConfig, cmd, "", commandMode, codePage)
		manager.Cmd_C(batPath, parseResult.WorkDir)
	} else {
		manager.ExplorerOpenDir(parseResult.ValidPath)
	}
}

// SelectFileOrDir 打开explorer并选中指定文件或目录
func SelectFileOrDir(appConfig *config.AppConfig, req *vo.PathReq, bins [][]byte) {
	parseResult := pathutil.ParseValidPath(req.Path, req.CtxDir)
	if !parseResult.Succ {
		panic(fmt.Sprintf("未识别到有效路径：\r\n%s", req.Path))
	}
	manager.ExplorerSelectItem(parseResult.ValidPath)
}

// OpenAs 调用打开方式
// 对网址，不做校验直接调用
// 否则先校验路径有效性，再调用
func OpenAs(appConfig *config.AppConfig, req *vo.PathReq, bins [][]byte) {
	lowerPath := strings.TrimSpace(strings.ToLower(req.Path))
	if strings.HasPrefix(lowerPath, "https://") || strings.HasPrefix(lowerPath, "http://") {
		manager.OpenAs(req.Path)
		return
	}
	parseResult := pathutil.ParseValidPath(req.Path, req.CtxDir)
	if !parseResult.Succ {
		panic(fmt.Sprintf("未识别到有效路径：\r\n%s", req.Path))
	}
	if parseResult.Dir {
		panic(fmt.Sprintf("目录不支持打开方式操作：\r\n%s", parseResult.ValidPath))
	}
	manager.OpenAs(parseResult.ValidPath)
}

// OpenIn 以指定路径为工作目录打开指定文件
func OpenIn(appConfig *config.AppConfig, req *vo.OpenInReq, bins [][]byte) {
	fileParseResult := pathutil.ParseValidPath(req.Path, req.CtxDir)
	if !fileParseResult.Succ || fileParseResult.Dir {
		panic(fmt.Sprintf("无法识别文件路径：\r\n%s", req.Path))
	}
	dirParseResult := pathutil.ParseValidPath(req.Dir, "")
	if !dirParseResult.Succ {
		panic(fmt.Sprintf("无法识别目录路径：\r\n%s", req.CtxDir))
	}
	dir := dirParseResult.ValidPath
	if dirParseResult.File {
		dir = filepath.Dir(dir)
	}

	// 指定文件是cmd，直接调用cmdopen
	if strings.TrimSpace(strings.ToLower(pathutil.GetCmdPath())) == strings.TrimSpace(strings.ToLower(fileParseResult.ValidPath)) {
		runnerservice.OpenCmdWithWorkDir(appConfig, &vo.PathReq{Path: dir}, bins)
		return
	}

	// 否则直接执行，如果是批处理则弹出执行，否则不弹出
	cmd := fmt.Sprintf(`"%s"`, fileParseResult.ValidPath)
	batPath := manager.SaveCmdFile(appConfig, cmd, "", pathutil.IsBatFile(fileParseResult.ValidPath))
	manager.Cmd_C(batPath, dir)
}

// OpenBy 用指定打开方式打开指定的内容（文件、网址、纯文本）
func OpenBy(appConfig *config.AppConfig, req *vo.OpenByReq, bins [][]byte) {
	req.Path = strings.TrimSpace(req.Path)
	req.Opener = strings.TrimSpace(req.Opener)

	// 目标路径处理，如果是网址，则不验证有效性；
	// 如果是纯文本标识符，则也不验证有效性，把前缀去掉待处理；
	// 否则验证路径有效性
	targetPath := ""
	isFile := false
	isDir := false

	if req.UseDir {
		parseResult := pathutil.ParseValidPath(req.Path, req.CtxDir)
		if !parseResult.Succ || !parseResult.Dir {
			panic(fmt.Sprintf("目录不存在，无法打开：\r\n%s", req.Path))
		}
		targetPath = parseResult.ValidPath
		isDir = true
	} else {
		if strings.HasPrefix(req.Path, "http://") || strings.HasPrefix(req.Path, "https://") {
			targetPath = req.Path
		} else if strings.HasPrefix(req.Path, "txt://") {
			targetPath = strings.TrimPrefix(strings.TrimPrefix(req.Path, "txt:///"), "txt://")
		} else {
			parseResult := pathutil.ParseValidPath(req.Path, req.CtxDir)
			if !parseResult.Succ || !parseResult.File {
				panic(fmt.Sprintf("文件不存在，无法打开：\r\n%s", req.Path))
			}
			targetPath = parseResult.ValidPath
			isFile = true
		}
	}

	// 打开方式处理
	// 如果是固定名称photo，则使用图片传真查看器打开，路径不需要任何包裹，否则会出错
	if OPENER_PHOTO == req.Opener {
		cmdTxt := manager.CreateOpenByPhotoViewerCmd(targetPath)
		batPath := manager.SaveCmdFile(appConfig, cmdTxt, "", false)
		manager.Cmd_C(batPath, "")
		return
	}
	// 其它情况下，路径进行指定的包裹处理
	targetPath = handleTargetPath(targetPath, isFile, isDir, req.Option)
	// 是命令
	if strings.Contains(req.Opener, "%1") {
		cmd := strings.ReplaceAll(strings.ReplaceAll(req.Opener, "%1", targetPath), " \\", "\n")
		batPath := manager.SaveCmdFile(appConfig, cmd, "", true)
		manager.Cmd_C(batPath, "")
		return
	}
	// 否则认为是文件，按文件路径解析
	// 如果是批处理文件，则弹出执行，否则不弹出
	parseResult := pathutil.ParseValidPath(req.Opener, "")
	if !parseResult.Succ || !parseResult.File {
		panic(fmt.Sprintf("未找到指定的打开方式：\r\n%s", req.Opener))
	}
	useStart := pathutil.IsBatFile(parseResult.ValidPath)
	cmd := fmt.Sprintf(`"%s" %s`, parseResult.ValidPath, targetPath)
	batPath := manager.SaveCmdFile(appConfig, cmd, "", useStart)
	manager.Cmd_C(batPath, parseResult.WorkDir)
}

// handleTargetPath 按指定选项对路径处理
func handleTargetPath(path string, isFile, isDir bool, option string) string {
	path = strings.TrimSpace(path)
	if "" == option {
		return path
	}
	if OPTION_WRAP == option {
		if strings.Contains(path, " ") {
			return `"` + path + `"`
		}
	}
	if OPTION_FORCEWRAP == option {
		return `"` + path + `"`
	}
	if OPTION_WRAPIN == option {
		return pathutil.WrapQuotOnPathItems(path)
	}
	return path
}
